package com.laf.manage.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.laf.common.constant.Constants;
import com.laf.common.constant.HttpStatus;
import com.laf.common.constant.LostItemConstant;
import com.laf.common.constant.MQConstant;
import com.laf.common.core.domain.entity.MobileUser;
import com.laf.common.core.domain.model.LoginMobileUser;
import com.laf.common.core.domain.model.LoginUser;
import com.laf.common.core.page.TableDataInfo;
import com.laf.common.core.redis.RedisCache;
import com.laf.common.exception.ServiceException;
import com.laf.common.utils.SecurityUtils;
import com.laf.common.utils.StringUtils;
import com.laf.common.utils.mq.RabbitMqHelper;
import com.laf.common.utils.push.PushMsgUtils;
import com.laf.common.utils.qrCode.MyQrCodeUtil;
import com.laf.common.utils.uuid.IdUtils;
import com.laf.manage.constant.LostItemStatusConstant;
import com.laf.manage.domain.doc.LostItemDoc;
import com.laf.manage.domain.dto.*;
import com.laf.manage.domain.po.AuthInfo;
import com.laf.manage.domain.po.Category;
import com.laf.manage.domain.po.LostItem;
import com.laf.manage.domain.po.ReceiveRecord;
import com.laf.manage.domain.query.LostItemQuery;
import com.laf.manage.domain.vo.ItemDetailsVO;
import com.laf.manage.domain.vo.LostItemVO;
import com.laf.manage.enums.ItemStatusEnum;
import com.laf.manage.enums.ProofTypeEnum;
import com.laf.manage.enums.QrTypeEnum;
import com.laf.manage.mapper.es.LostItemEsMapper;
import com.laf.manage.mapper.mp.AuthInfoMapper;
import com.laf.manage.mapper.mp.LostItemMapper;
import com.laf.manage.mapper.mp.ReceiveRecordMapper;
import com.laf.manage.service.ICategoryService;
import com.laf.manage.service.ILostItemService;
import com.laf.mobile.mapper.MobileUserMapper;
import org.dromara.easyes.core.biz.EsPageInfo;
import org.dromara.easyes.core.conditions.select.LambdaEsQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 失物管理Service业务层处理
 *
 * @author zian
 * @date 2024-11-01
 */
@Service
public class LostItemServiceImpl extends ServiceImpl<LostItemMapper, LostItem> implements ILostItemService {

    @Autowired
    private LostItemMapper lostItemMapper;

    @Autowired
    private LostItemEsMapper lostItemEsMapper;

    @Autowired
    private RabbitMqHelper rabbitMqHelper;

    @Autowired
    private MyQrCodeUtil qrCodeUtil;

    @Autowired
    private RedisCache redisCache;

    @Autowired
    private ICategoryService categoryService;

    @Autowired
    private ReceiveRecordMapper receiveRecordMapper;

    @Autowired
    private AuthInfoMapper authInfoMapper;

    @Autowired
    private LostItemMessageService lostItemMessageService;

    @Autowired
    private MobileUserMapper userMapper;

    @Autowired(required = false)
    private PushMsgUtils pushMsgUtils;

    /**
     * 移动端用户获取失物列表
     *
     * @param query 查询条件
     * @return
     */
    @Override
    public PageDTO<LostItemVO> getLostItemList(LostItemQuery query) {

        // 查询数据
        EsPageInfo<LostItemDoc> esPageInfo = lostItemEsMapper
                .pageQuery(buildQuery(query), query.getPageNo(), query.getPageSize());

        // 转换为VO
        List<LostItemDoc> list = esPageInfo.getList();
        List<LostItemVO> voList = BeanUtil.copyToList(list, LostItemVO.class);
        if (CollUtil.isEmpty(voList)) {
            return PageDTO.empty(0L, 0L);
        }

        // 收集创建人id , 用于获取用户头像
        List<Long> userIds = list.stream()
                .map(LostItemDoc::getCreateUserId)
                .collect(Collectors.toList());

        //  收集用户数据
        List<MobileUser> users = userMapper.selectBatchIds(userIds);

        // 用户id 和 用户信息
        Map<Long, MobileUser> map = users.stream().collect(Collectors.toMap(MobileUser::getUserId, item -> item));
        for (LostItemVO vo : voList) {
            MobileUser item = map.get(vo.getCreateUserId());
            if (item != null) {
                vo.setAvatar(item.getAvatar());
                vo.setNickName(item.getNickName());
            }
            vo.setItemUrl(null);
        }
        return PageDTO.of(esPageInfo.getTotal(), (long) esPageInfo.getPages(), voList);
    }

    @Override
    public String generateCode(Long id, QrTypeEnum qrTypeEnum) {
        // 1. 检查物品是否存在
        LostItem lostItem = getById(id);
        if (lostItem == null) throw new ServiceException("物品不存在");
        if (!lostItem.getStatus().equals(qrTypeEnum.getStatus()))
            throw new ServiceException(qrTypeEnum.equals(QrTypeEnum.SUBMIT_CODE) ? "当前状态不允许提交" : "当前状态不允许领取");

        // 2. 封装二维码的信息
        String uuid = IdUtils.simpleUUID();
        Long userId = SecurityUtils.getMobileUserId();
        assert userId != null;
        ItemQrCodeDto dto = new ItemQrCodeDto(userId.toString(), id.toString(), lostItem.getStatus(), uuid);

        // 3. 保存验证信息
        redisCache.setCacheObject(qrTypeEnum.getKeyPrefix() + uuid, dto, 30, TimeUnit.MINUTES);

        // 4. 生成二维码
        return qrCodeUtil.createQrCode(JSONUtil.toJsonStr(dto));
    }

    @Override
    public List<StatusCountDTO> lostCount() {
        return getBaseMapper().countByStatus();
    }


    /**
     * 移动端构建查询条件
     *
     * @param query 查询条件
     */
    public LambdaEsQueryWrapper<LostItemDoc> buildQuery(LostItemQuery query) {
        LambdaEsQueryWrapper<LostItemDoc> wrapper = new LambdaEsQueryWrapper<>();
        // 关键字
        if (StrUtil.isNotBlank(query.getKey())) {
            wrapper.multiMatchQuery(
                    query.getKey(),
                    LostItemDoc::getAnswerOne,
                    LostItemDoc::getAnswerTwo,
                    LostItemDoc::getItemName,
                    LostItemDoc::getDescription,
                    LostItemDoc::getPickUpLocation
            );
        }
        if (query.getStatus() == null) {
            query.setStatus(LostItemStatusConstant.WAIT_PICKED);
        }
        // 分类
        wrapper.eq(query.getCategoryId() != null, LostItemDoc::getCategoryId, query.getCategoryId());
        // 状态
        wrapper.eq(query.getStatus() != null, LostItemDoc::getStatus, query.getStatus());
        // 点位
        wrapper.eq(query.getPointId() != null, LostItemDoc::getPointId, query.getPointId());
        // 报告人
        wrapper.eq(query.getCreateUserId() != null, LostItemDoc::getCreateUserId, query.getCreateUserId());

        // 设置按照创建时间排序
        wrapper.orderByDesc(LostItemDoc::getCreateTime);
        return wrapper;
    }

    /**
     * 管理端查询失物管理列表
     *
     * @param lostItem 失物管理
     * @return 失物管理
     */
    @Override
    public List<LostItem> selectLostItemList(LostItem lostItem) {
        List<LostItem> lostItemList = lostItemMapper.selectList(buildQueryWrapper(lostItem));
        return lostItemList;
    }

    /**
     * 构建查询条件
     *
     * @param query
     * @return
     */
    private LambdaQueryWrapper<LostItem> buildQueryWrapper(LostItem query) {
        Map<String, Object> params = query.getParams();
        LambdaQueryWrapper<LostItem> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(query.getItemName()), LostItem::getItemName, query.getItemName());
        lqw.eq(StringUtils.isNotBlank(query.getAnswerOne()), LostItem::getAnswerOne, query.getAnswerOne());
        lqw.eq(StringUtils.isNotBlank(query.getAnswerTwo()), LostItem::getAnswerTwo, query.getAnswerTwo());
        lqw.eq(StringUtils.isNotBlank(query.getDescription()), LostItem::getDescription, query.getDescription());
        lqw.eq(StringUtils.isNotBlank(query.getPickUpLocation()), LostItem::getPickUpLocation, query.getPickUpLocation());
        lqw.eq(query.getCategoryId() != null, LostItem::getCategoryId, query.getCategoryId());
        lqw.eq(query.getPointId() != null, LostItem::getPointId, query.getPointId());
        lqw.eq(query.getStatus() != null && !query.getStatus().equals(LostItemConstant.ALL), LostItem::getStatus, query.getStatus());
        lqw.orderByDesc(LostItem::getUpdateBy);
        return lqw;
    }

    /**
     * 管理端查询失物-展示
     *
     * @param lostItem 失物管理
     * @param pageNum  页码
     * @param pageSize 页大小
     * @return TableDataInfo
     */
    @Override
    public TableDataInfo selectLostItemList(LostItem lostItem, Integer pageNum, Integer pageSize) {
        LambdaEsQueryWrapper<LostItemDoc> wrapper = buildEsQueryWrapper(lostItem);

        // 执行查询
        EsPageInfo<LostItemDoc> lostItemDocEsPageInfo = lostItemEsMapper.pageQuery(wrapper, pageNum, pageSize);
        List<LostItemDoc> list = lostItemDocEsPageInfo.getList();
        if (CollUtil.isEmpty(list)) return TableDataInfo.empty();
        for (LostItemDoc doc : list) {
            doc.setCreateBy(DesensitizedUtil.mobilePhone(doc.getCreateBy()));
        }
        // 封装返回值
        TableDataInfo tableDataInfo = new TableDataInfo();
        tableDataInfo.setTotal(lostItemDocEsPageInfo.getTotal());
        tableDataInfo.setRows(list);
        tableDataInfo.setCode(HttpStatus.SUCCESS);
        tableDataInfo.setMsg("查询成功");
        return tableDataInfo;
    }

    /**
     * 管理员条件构建查询
     *
     * @param lostItem
     * @return
     */
    private static LambdaEsQueryWrapper<LostItemDoc> buildEsQueryWrapper(LostItem lostItem) {
        LambdaEsQueryWrapper<LostItemDoc> wrapper = new LambdaEsQueryWrapper<>();
        // 构建查询条件
        // 状态
        wrapper.eq(lostItem.getStatus() != null && !lostItem.getStatus().equals(LostItemConstant.ALL), LostItemDoc::getStatus, lostItem.getStatus());
        // 点位
        wrapper.eq(lostItem.getPointId() != null, LostItemDoc::getPointId, lostItem.getPointId());
        // 分类
        wrapper.eq(lostItem.getCategoryId() != null, LostItemDoc::getCategoryId, lostItem.getCategoryId());
        // 修改人
        wrapper.eq(StrUtil.isNotBlank(lostItem.getUpdateBy()), LostItemDoc::getUpdateBy, lostItem.getUpdateBy());

        // 关键字：itemName 、 description 、 pickUpLocation ， answerOne answerTwo
        StringBuilder keyword = new StringBuilder();
        if (StringUtils.isNotBlank(lostItem.getItemName())) {
            keyword.append(lostItem.getItemName());
        }
        if (StringUtils.isNotBlank(lostItem.getDescription())) {
            keyword.append(lostItem.getDescription());
        }
        if (StringUtils.isNotBlank(lostItem.getPickUpLocation())) {
            keyword.append(lostItem.getPickUpLocation());
        }


        if (StringUtils.isNotBlank(keyword.toString())) {
            wrapper.multiMatchQuery(keyword.toString(),
                    LostItemDoc::getItemName,
                    LostItemDoc::getDescription,
                    LostItemDoc::getPickUpLocation,
                    LostItemDoc::getAnswerOne,
                    LostItemDoc::getAnswerTwo
            );
        }

        // 问答
        if (lostItem.getCategoryId() != null) {
            wrapper.match(StrUtil.isNotBlank(lostItem.getAnswerOne()), LostItemDoc::getAnswerOne, lostItem.getAnswerOne());
            wrapper.match(StrUtil.isNotBlank(lostItem.getAnswerTwo()), LostItemDoc::getAnswerTwo, lostItem.getAnswerTwo());
        }
        return wrapper;
    }

    /**
     * 根据id获取失物详情
     *
     * @param id
     * @param isAdmin
     * @return
     */
    @Override
    public ItemDetailsVO getDetailsById(Long id, boolean isAdmin) {
        ItemDetailsVO vo = getBaseMapper().getLostItemDetailsById(id);
        if (vo == null) throw new ServiceException("物品不存在");
        // 如果是本人提交展示原图即可 , 如果不是本人提交，则隐藏原图
        if (!isAdmin) {
            Long userId = SecurityUtils.getMobileUserId();
            if (!vo.getCreateUserId().equals(userId)) {
                vo.setItemUrls(null);
                vo.setCreateBy(DesensitizedUtil.mobilePhone(vo.getCreateBy()));
            }
            if (!vo.getCreateUserId().equals(userId) && !vo.getStatus().equals(ItemStatusEnum.WAIT_PICKUP.getCode())) {
                throw new ServiceException("没有查看权限");
            }

        }

        return vo;
    }

    /**
     * 更新失物信息
     *
     * @param dto     更新失物信息
     * @param isAdmin
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean editLostItem(LostItemDTO dto, boolean isAdmin) {
        //1. 检查失物是否存在
        LostItem lostItem = getById(dto.getId());
        if (lostItem == null) throw new ServiceException("物品不存在");
        String updateBy;
        Integer status = lostItem.getStatus();

        // 对物品的状态进行判断，来确保用户或者管理员有权限修改
        if (isAdmin) {
            // 管理员
            updateBy = SecurityUtils.getUsername();
            // 只有待领取物品管理才能修改
            if (!status.equals(LostItemConstant.WAIT_PICKED)) {
                throw new ServiceException("当前状态的物品不允许修改");
            }
        } else {
            // 普通用户
            LoginMobileUser mobileLoginUser = SecurityUtils.getMobileLoginUser();
            updateBy = mobileLoginUser.getUsername();
            if (!lostItem.getCreateUserId().equals(mobileLoginUser.getUserId())) {
                throw new ServiceException("没有权限修改");
            }
            if (!status.equals(LostItemConstant.WAIT_SUBMIT) && !status.equals(LostItemConstant.WAIT_AUDIT)) {
                throw new ServiceException("当前状态的物品不允许修改");
            }

        }

        // 保存旧的数据，用来检查是否需要重新生成灰度图
        List<String> oldItemUrls = lostItem.getItemUrls();
        Long oldCategoryId = lostItem.getCategoryId();

        // 2 .更新 mysql
        lostItem.setItemName(dto.getItemName());
        lostItem.setItemUrls(dto.getItemUrls());
        lostItem.setPickUpLocation(dto.getPickUpLocation());
        lostItem.setPointId(dto.getPointId());
        lostItem.setCategoryId(dto.getCategoryId());
        lostItem.setAnswerOne(dto.getAnswerOne());
        lostItem.setAnswerTwo(dto.getAnswerTwo());
        lostItem.setDescription(dto.getDescription());
        lostItem.setUpdateBy(updateBy);
        lostItem.setUpdateTime(LocalDateTime.now());
        boolean b = updateById(lostItem);
        if (!b) throw new ServiceException("更新失败");

        // 3. 更新 es 中数据
        updateLostItemDoc(lostItem);

        // 管理修改的都是待领取的物品，待领取物品修改可能要重新生成灰度图
        if (isAdmin) {
            // 4. 判断图片是否修改 或者分类是否被修改 ， 只要有一项发生改变就重新生成图片
            if (!dto.getItemUrls().equals(oldItemUrls)
                    || !dto.getCategoryId().equals(oldCategoryId)) {
                Category category = categoryService.getById(dto.getCategoryId());
                // 重新生成展示图片
                GrayscaleDTO grayscaleDTO = new GrayscaleDTO(lostItem.getId(), Constants.DEFAULT_IMAGE_VAL.equals(category.getIsDefaultImage()));
                rabbitMqHelper.sendMessage(
                        MQConstant.LOST_ITEM_EXCHANGE,
                        MQConstant.LOST_ITEM_GRAYSCALE_GENERATION_ROUTING_KEY,
                        grayscaleDTO
                );
            }
        }

        return true;
    }

    /**
     * 更新 es 数据
     *
     * @param lostItem 使用全量更新
     */
    private void updateLostItemDoc(LostItem lostItem) {
        // 4. 更新 es
        LostItemDoc doc = lostItemEsMapper.selectById(lostItem.getId());

        // 封装 doc
        LostItemDoc insertDoc = BeanUtil.copyProperties(lostItem, LostItemDoc.class);
        insertDoc.setItemUrl(lostItem.getItemUrls().get(0));
        if (lostItem.getShowUrls() != null && !lostItem.getShowUrls().isEmpty()) {
            insertDoc.setShowUrl(lostItem.getShowUrls().get(0));

        }
        // 不存在新增
        if (doc == null) {
            // 发送消息新增es数据
            lostItemMessageService.sendAddLostItemMsg(lostItem.getId(), lostItem.getStatus());
          /*  Integer result = lostItemEsMapper.insert(insertDoc);
            if (result <= 0)
                throw new ServiceException(" LostItemListener.addLostItemToEs 写入es 失败 ，id: " + lostItem.getId());*/
        } else {
            // 存在修改 , 全量修改更具id
            Integer result = lostItemEsMapper.updateById(insertDoc);
            if (result <= 0)
                throw new ServiceException(" LostItemListener.updateLostItemToEs 写入es 失败 ，id: " + lostItem.getId());
        }
    }

    /**
     * 新增失物
     *
     * @param dto
     * @param isAdmin
     * @return
     */
    @Override
    public boolean addLostItem(LostItemDTO dto, boolean isAdmin) {
        // 补充信息 1. status 、2. create_by 3. create_time 4. update_by , 5. update_time
        LostItem item = BeanUtil.toBean(dto, LostItem.class);

        // 补充提交人信息
        if (isAdmin) {
            LoginUser loginUser = SecurityUtils.getLoginUser();
            item.setCreateBy(loginUser.getUsername());
            item.setUpdateBy(loginUser.getUsername());
            item.setCreateUserId(loginUser.getUserId());
            item.setRemark(loginUser.getUsername() + " : 管理员提交");
            item.setStatus(LostItemConstant.WAIT_PICKED);
        } else {
            LoginMobileUser loginMobileUser = SecurityUtils.getMobileLoginUser();
            item.setCreateBy(loginMobileUser.getUsername());
            item.setUpdateBy(loginMobileUser.getUsername());
            item.setCreateUserId(loginMobileUser.getUserId());
            item.setStatus(LostItemConstant.WAIT_AUDIT);
        }

        LocalDateTime now = LocalDateTime.now();
        item.setCreateTime(now);
        item.setUpdateTime(now);

        // 2.新增数据库数据
        boolean b = save(item);
        if (!b) throw new ServiceException("添加失败");

        // 3. 发送消息到 mq 中
        lostItemMessageService.sendAddLostItemMsg(item.getId(), item.getStatus());
        return false;
    }


    /**
     * 审核通过
     *
     * @param id 物品id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务
    public boolean passAudit(Long id) {
        LostItem lostItem = updateStatus(id, LostItemConstant.WAIT_AUDIT, LostItemConstant.WAIT_SUBMIT);
        // TODO 发送消息 push
       /* MyPushDTO pushDTO = new MyPushDTO();
        MobileUser mobileUser = userMapper.selectById(lostItem.getCreateUserId());
        pushDTO.setCId(mobileUser.getCId());
        pushDTO.setTitle("物品审核通过");
        pushDTO.setContent(lostItem.getItemName() + "审核通过");
        pushDTO.setType(PushTypeEnum.MESSAGE_PUSH);
        pushDTO.setTitle("物品审核通过");
        pushMsgUtils.pushBatchByCid(pushDTO);*/
        return true;
    }


    /**
     * 通过提交
     *
     * @param id id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务
    public boolean submitPass(Long id) {
        // 1. 更新状态
        LostItem lostItem = updateStatus(id, LostItemConstant.WAIT_SUBMIT, LostItemConstant.WAIT_PICKED);
        //2. 查询分类，
        Category category = categoryService.getById(lostItem.getCategoryId());
        if (category == null) throw new ServiceException("分类不存在");
        GrayscaleDTO grayscaleDTO = new GrayscaleDTO(lostItem.getId(), Constants.DEFAULT_IMAGE_VAL.equals(category.getIsDefaultImage()));


        // 2. 发送消息，给通过提交的物品生成灰度图
        rabbitMqHelper.sendMessage(
                MQConstant.LOST_ITEM_EXCHANGE,
                MQConstant.LOST_ITEM_GRAYSCALE_GENERATION_ROUTING_KEY,
                grayscaleDTO
        );
        return true;
    }

    /**
     * 删除 mysql 数据和删除 es 中数据
     *
     * @param list
     */
    @Override
    @Transactional
    public void deleteByIds(List<Long> list) {
        // 删除 mysql
        boolean b = removeByIds(list);
        if (!b) throw new ServiceException("删除失败");

        // 删除 es
        Integer result = lostItemEsMapper.deleteBatchIds(list);
        if (result <= 0) throw new ServiceException("删除失败");
    }

    /**
     * 校验二维码
     *
     * @param uuid      uuid
     * @param keyPrefix 对应的前缀
     */
    @Override
    public void checkCode(String uuid, String keyPrefix) {
        // 判断是否存在
        Boolean b = redisCache.hasKey(keyPrefix + uuid);
        if (!b) throw new ServiceException("二维码已过期");

        // 校验成功删除缓存
        redisCache.deleteObject(keyPrefix + uuid);
    }


    /**
     * 驳回物品
     *
     * @param dto     有 id 和 驳回理由
     * @param isAdmin 是否是管理员
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollback(RollbackDTO dto, boolean isAdmin) {
        // 1. 检查数据
        LostItem lostItem = getById(dto.getId());
        if (lostItem == null) throw new ServiceException("物品不存在");

        // 2. 检查失物状态，只有在待审核 和 待提交物品可驳回
        Integer status = lostItem.getStatus();
        if (!status.equals(LostItemConstant.WAIT_AUDIT) && !status.equals(LostItemConstant.WAIT_SUBMIT)) {
            throw new ServiceException("当前状态的物品不允许驳回");
        }

        String username;
        if (isAdmin) {
            username = SecurityUtils.getUsername();
            dto.setRemark("管理员驳回：" + dto.getRemark());
        } else {
            username = SecurityUtils.getMobileUsername();
            dto.setRemark("用户撤回：" + dto.getRemark());
            if (!lostItem.getCreateUserId().equals(SecurityUtils.getMobileUserId())) {
                throw new ServiceException("物品不存在");
            }
        }
        LocalDateTime now = LocalDateTime.now();
        // 2. 更新 mysql
        boolean b = lambdaUpdate()
                .eq(LostItem::getId, dto.getId())
                .set(LostItem::getStatus, LostItemConstant.REJECTED)
                .set(LostItem::getUpdateBy, username)
                .set(LostItem::getUpdateTime, now)
                .set(LostItem::getRemark, dto.getRemark())
                .update();
        if (!b) throw new ServiceException("驳回失败");


        // 3. 查询 es 中是否存在
        LostItemDoc lostItemDoc = lostItemEsMapper.selectById(dto.getId());
        // 3.1 如果不存在，就新增
        if (lostItemDoc == null) {
            // 新增 ，物品状态要是待提交的
            lostItemMessageService.sendAddLostItemMsg(lostItem.getId(), LostItemConstant.REJECTED);
        } else {
            // 3.3 修改状态
            lostItemDoc.setStatus(LostItemConstant.REJECTED);
            lostItemDoc.setUpdateTime(now);
            Integer result = lostItemEsMapper.updateById(lostItemDoc);
            if (result <= 0) throw new ServiceException("审核失败，更新 es 失败");
        }

        // TODO 发送消息 push
    /*    MyPushDTO pushDTO = new MyPushDTO();
        MobileUser mobileUser = userMapper.selectById(lostItem.getCreateUserId());
        pushDTO.setCId(mobileUser.getCId());
        pushDTO.setTitle("物品审核驳回");
        pushDTO.setContent(lostItem.getItemName() + "审核驳回");
        pushDTO.setType(PushTypeEnum.MESSAGE_PUSH);
        pushMsgUtils.pushBatchByCid(pushDTO);*/
    }

    /**
     * 领取物品
     *
     * @param dto 领取信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void receiveItem(ReceiveDTO dto) {
        // 1. 校验数据
        ProofTypeEnum proofTypeEnum = ProofTypeEnum.getByCode(dto.getProofType());
        if (proofTypeEnum == null) throw new ServiceException("凭证类型错误");

        // 2. 更新 mysql 和 es
        LostItem lostItem = updateStatus(dto.getItemId(), LostItemConstant.WAIT_PICKED, LostItemConstant.PICKED);
        // 3. 新增领取记录
        insertReceive(dto, proofTypeEnum, lostItem);

    }


    /**
     * 插入领取记录
     *
     * @param dto           接收数据
     * @param proofTypeEnum 凭证类型
     * @param lostItem      物品信息
     */
    private void insertReceive(ReceiveDTO dto, ProofTypeEnum proofTypeEnum, LostItem lostItem) {
        // 4. 新增领取记录
        ReceiveRecord record = new ReceiveRecord();

        // 当凭证类型是身份的时候，需要查询认证信息表
        if (proofTypeEnum.getCode().equals(ProofTypeEnum.ID_CARD.getCode())) {
            AuthInfo authInfo = authInfoMapper.selectOne(
                    Wrappers.<AuthInfo>lambdaQuery()
                            .eq(AuthInfo::getUserId, dto.getUserId())
            );
            if (authInfo == null) throw new ServiceException("用户未认证");
            dto.setProofUrl(authInfo.getIdNumberUrl());
            dto.setRealName(authInfo.getRealName());
        }

        String username = SecurityUtils.getUsername();
        LocalDateTime now = LocalDateTime.now();
        // 补充其他信息
        record.setId(null);
        record.setItemId(lostItem.getId());
        record.setItemName(lostItem.getItemName());
        record.setItemUrls(lostItem.getItemUrls());
        record.setPickUpLocation(lostItem.getPickUpLocation());
        record.setCategoryId(lostItem.getCategoryId());
        record.setUserId(dto.getUserId());
        record.setPointId(lostItem.getPointId());
        record.setProofType(proofTypeEnum.getCode());
        record.setRealName(dto.getRealName());
        record.setProofUrl(dto.getProofUrl());

        record.setCreateBy(username);
        record.setUpdateBy(username);
        record.setCreateTime(now);
        record.setUpdateTime(now);
        int result = receiveRecordMapper.insert(record);
        if (result <= 0) throw new ServiceException("领取失败");

        //  同步 es 数据
        rabbitMqHelper.sendMessage(
                MQConstant.RECEIVE_RECORD_EXCHANGE,
                MQConstant.LOST_ITEM_RECORD_ROUTING_KEY,
                record.getId()
        );

    }


    /**
     * 更新状态
     *
     * @param id        物品id
     * @param oldStatus 旧的状态 （用于校验的状态）
     * @param newStatus 新的状态 ( 更新之后的状态 )
     * @return 是否成功
     */
    private LostItem updateStatus(Long id, Integer oldStatus, Integer newStatus) {
        // 1. 检查 mysql 中是否存在
        LostItem lostItem = getById(id);
        if (lostItem == null) throw new ServiceException("物品不存在");
        // 2. 检查失物状态
        if (!lostItem.getStatus().equals(oldStatus)) {
            throw new ServiceException("当前状态的物品不允许审核");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        LocalDateTime now = LocalDateTime.now();

        //2. 更新 mysql 中状态
        boolean b = this.lambdaUpdate()
                .eq(LostItem::getId, id)
                .set(LostItem::getStatus, newStatus)
                .set(LostItem::getUpdateBy, loginUser.getUsername())
                .set(LostItem::getUpdateTime, now)
                .update();
        if (!b) throw new ServiceException("审核失败");


        // 3. 查询 es 中是否存在
        LostItemDoc lostItemDoc = lostItemEsMapper.selectById(id);
        // 3.1 如果不存在，就新增
        if (lostItemDoc == null) {
            // 发送信息新增失物，抽取到一个独立的类，原因是需要使用事务的传播行为，否则会导致 es 和 mysql 数据不一致
            // 先要保证 mysql 成功提交失物了，在发送消息这样在监听器中获取的数据才是最新的数据
            lostItemMessageService.sendAddLostItemMsg(lostItem.getId(), newStatus);
        } else {
            // 3.3 修改状态
            lostItemDoc.setStatus(newStatus);
            lostItemDoc.setUpdateTime(now);
            Integer result = lostItemEsMapper.updateById(lostItemDoc);
            if (result <= 0) throw new ServiceException("状态为：" + newStatus + "，更新 es 失败");
        }

        return lostItem;
    }


}
